#pragma once

#include "Common.hpp"
#include "ObjectPool.hpp"
#include "PageMap.hpp"

class PageCache
{
public:
	static PageCache* GetInstance()
	{
		return &_sInst;
	}
	std::mutex& GetMtx()
	{
		return _pageMtx;
	}

	//��ȡһ��kҳ��Span
	Span* NewSpan(size_t k)
	{
		assert(k > 0);

		if (k > NPAGES - 1)
		{
			void* ptr = SystemAlloc(k);
			//Span* span = new Span;
			Span* span = _spanPool.New();
			span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
			span->_n = k;

			//_idSpanMap[span->_pageId] = span;
			_idSpanMap.set(span->_pageId, span);
			return span;
		}

		//�ȼ���k��Ͱ����û��span
		if (!_spanLists[k].Empty())
		{

			Span* kSpan = _spanLists[k].PopFront();
			//����_pageId��Span��ӳ��
			for (PAGE_ID i = 0; i < kSpan->_n; i++)
			{
				//_idSpanMap[kSpan->_pageId + i] = kSpan;
				_idSpanMap.set(kSpan->_pageId + i, kSpan);
			}
			return kSpan;
		}
		//�������Ͱ����û�д��Span
		for (size_t i = k + 1; i < NPAGES; i++)
		{
			if (!_spanLists[i].Empty())
			{
				Span* nSpan = _spanLists[i].PopFront();
				//Span* kSpan = new Span;
				Span* kSpan = _spanPool.New();
				//��nSpan��ͷ���г�һ��kҳ����
				kSpan->_pageId = nSpan->_pageId;
				kSpan->_n = k;

				nSpan->_pageId += k;
				nSpan->_n -= k;
				//���зֺõķ��أ�ʣ�µ���һ�����ڶ�Ӧλ�ã�
				_spanLists[nSpan->_n].PushFront(nSpan);
				//�洢nSpan����βҳ�Ÿ�nSpan��ӳ�䣬�������PageCache���еĺϲ�����
				/*_idSpanMap[nSpan->_pageId] = nSpan;
				_idSpanMap[nSpan->_pageId + nSpan->_n - 1] = nSpan;*/
				_idSpanMap.set(nSpan->_pageId, nSpan);
				_idSpanMap.set(nSpan->_pageId + nSpan->_n - 1, nSpan);

				//����_pageId��Span��ӳ��
				for (PAGE_ID i = 0; i < kSpan->_n; i++)
				{
					//_idSpanMap[kSpan->_pageId + i] = kSpan;
					_idSpanMap.set(kSpan->_pageId + i, kSpan);
				}
				return kSpan;
			}
		}
		//�������û�о���Ҫ�Ҷ�����һ��128ҳ��Span��
		//Span* bigSpan = new Span;
		Span* bigSpan = _spanPool.New();
		void* ptr = SystemAlloc(NPAGES - 1);
		bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
		bigSpan->_n = NPAGES - 1;

		_spanLists[bigSpan->_n].PushFront(bigSpan);
		//����ĽǶ����������ø���
		//��������ٶȺܿ죬�ڴε���NewSpan�����ڲ������ͼ���if�жϿ��Ժ��Բ���
		return NewSpan(k);
	}
	//��ȡ�Ӷ���span��ӳ��
	Span* MapObjectToSpan(void* obj)
	{
		PAGE_ID id = (PAGE_ID)obj >> PAGE_SHIFT;

		/*std::unique_lock<std::mutex> lock(_pageMtx);
		auto ret = _idSpanMap.find(id);
		if (ret != _idSpanMap.end())
			return ret->second;
		else
			assert(false);*/

		auto ret = _idSpanMap.get(id);
		assert(ret != nullptr);
		return (Span*)ret;
	}
	//�ͷſ��е�Span�ص�PageCache���ϲ����ڵ�Span�����Ի����ڴ���Ƭ����
	void ReleaseSpanToRageCache(Span* span)
	{
		if (span->_n > NPAGES - 1)
		{
			void* ptr = (void*)(span->_pageId << PAGE_SHIFT);
			SystemFree(ptr);
			_spanPool.Delete(span);
		}
		else
		{
			//��ǰ�ϲ�
			while (1)
			{
				PAGE_ID prevId = span->_pageId - 1;
				//auto ret = _idSpanMap.find(prevId);
				////ǰ��ı��û�ж�Ӧ��Span
				//if (ret == _idSpanMap.end())
				//	break;
				auto ret = (Span*)_idSpanMap.get(prevId);
				if (ret == nullptr)
					break;
				//ǰ���Span���ڱ���
				Span* prevSpan = ret;
				if (prevSpan->_isUse == true)
					break;
				//����Span�����128���޷����ӹ���
				if (prevSpan->_n + span->_n > NPAGES - 1)
					break;

				//�ϲ�
				span->_pageId = prevSpan->_pageId;
				span->_n += prevSpan->_n;

				_spanLists[prevSpan->_n].Erase(prevSpan);
				_spanPool.Delete(prevSpan);
			}
			//���ϲ�
			while (1)
			{
				PAGE_ID nextId = span->_pageId + span->_n;
				/*uto ret = _idSpanMap.find(nextId);
				if (ret == _idSpanMap.end())
					break;*/
				auto ret = (Span*)_idSpanMap.get(nextId);
				if (ret == nullptr)
					break;
				Span* nextSpan = ret;
				if (nextSpan->_isUse == true)
					break;
				if (nextSpan->_n + span->_n > NPAGES - 1)
					break;

				span->_n += nextSpan->_n;
				_spanLists[nextSpan->_n].Erase(nextSpan);
				_spanPool.Delete(nextSpan);
			}
			//���ϲ�����Span����Ӧ�õ�λ��
			_spanLists[span->_n].PushFront(span);
			span->_isUse = false;

			/*_idSpanMap[span->_pageId] = span;
			_idSpanMap[span->_pageId + span->_n - 1] = span;*/
			_idSpanMap.set(span->_pageId, span);
			_idSpanMap.set(span->_pageId + span->_n - 1, span);
		}
	}

private:
	PageCache() {}
	PageCache(const PageCache&) = delete;

private:
	SpanList _spanLists[NPAGES];   
	ObjectPool<Span> _spanPool;

	std::mutex _pageMtx;
	//std::unordered_map<PAGE_ID, Span*> _idSpanMap;
	//std::map<PAGE_ID, Span*> _idSpanMap;
	TCMalloc_PageMap1<32 - PAGE_SHIFT> _idSpanMap;
	

	static PageCache _sInst;
};

PageCache PageCache::_sInst;